# -*- coding: utf-8 -*-

#    OriginDB
#    Copyright (C) 2009  Jason Power <power.jg@gmail.com>
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License along
#    with this program; if not, write to the Free Software Foundation, Inc.,
#    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

from PyQt4 import QtCore, QtGui
from ui_reactionEdit import Ui_ReactionEditFrame

pressureConversions = {"atm":1, "Torr":760, "kPa":101.325}

class ReactionEdit(QtGui.QFrame):
	def __init__(self, controller, reactionClass, relationClass, catalystClass, mode='New'):
		QtGui.QFrame.__init__(self)

		self.controller = controller

		self.reactionClass = reactionClass
		self.relationClass = relationClass
		self.catalystClass = catalystClass

		self.reaction = None

        # Set up the user interface from Designer.
		self.ui = Ui_ReactionEditFrame()
		self.ui.setupUi(self)

		self.setMode(mode)

		self.setReadonly(not self.controller.userLoggedIn())

		self.reactants = []
		self.reactantPercents = []

		self.products = []
		self.productPercents = []

		self.catalysts = []
		self.catalystNames = []

		self.reference = None

		self.ui.pressureUnitsComboBox.addItems(["atm", "Torr", "kPa"])
		self.units = "atm"

		#hacks so I don't have to extend QListWidgetView
		self.ui.reactantList.dropEvent = self.customReactantDropEvent
		self.ui.productList.dropEvent = self.customProductDropEvent
		self.ui.catalystList.dropEvent = self.customCatalystDropEvent

		self.connect(self.ui.addPushButton, QtCore.SIGNAL("clicked()"), self.insertReaction)
		self.connect(self.ui.updatePushButton, QtCore.SIGNAL("clicked()"), self.updateReactionAction)

		self.connect(self.ui.searchReactantPushButton, QtCore.SIGNAL("clicked()"), self.searchReactant)
		self.connect(self.ui.searchProductPushButton, QtCore.SIGNAL("clicked()"), self.searchProduct)
		self.connect(self.ui.searchCatalystPushButton, QtCore.SIGNAL("clicked()"), self.searchCatalyst)
		self.connect(self.ui.newCatalystPushButton, QtCore.SIGNAL("clicked()"), self.newCatalyst)
		self.connect(self.ui.searchReferencePushButton, QtCore.SIGNAL("clicked()"), self.searchReference)
		self.connect(self.ui.newReferencePushButton, QtCore.SIGNAL("clicked()"), self.newReference)
		self.connect(self.ui.showAllCatalystsPushButton, QtCore.SIGNAL("clicked()"), self.showAllCatalysts)

		self.connect(self.ui.removeReactantPushButton, QtCore.SIGNAL("clicked()"), self.removeReactant)
		self.connect(self.ui.removeProductPushButton, QtCore.SIGNAL("clicked()"), self.removeProduct)
		self.connect(self.ui.removeCatalystPushButton, QtCore.SIGNAL("clicked()"), self.removeCatalyst)
		self.connect(self.ui.pressureUnitsComboBox, QtCore.SIGNAL("currentIndexChanged(const QString &)"), self.pressureUnitChanged)

		self.connect(self.ui.reactantList, QtCore.SIGNAL("itemDoubleClicked (QListWidgetItem *)"), self.reactantDoubleClick)
		self.connect(self.ui.productList, QtCore.SIGNAL("itemDoubleClicked (QListWidgetItem *)"), self.productDoubleClick)

	def clear(self):
		self.products = []
		self.productPercents = []

		self.reactants = []
		self.reactantPercents = []

		self.catalysts = []
		self.catalystNames = []

		self.ui.reactantList.clear()
		self.ui.productList.clear()
		self.ui.catalystList.clear()

		self.ui.referenceLineEdit.clear()
		self.ui.temperatureSpinBox.setValue(22)
		self.ui.pressureSpinBox.setValue(1)
		self.ui.pressureUnitsComboBox.setCurrentIndex(0)
		self.units = "atm"
		self.ui.timeSpinBox.setValue(0)
		self.ui.pHSpinBox.setValue(7)
		self.ui.solventLineEdit.clear()
		self.ui.solidCheckBox.setChecked(False)
		self.ui.liquidCheckBox.setChecked(False)
		self.ui.gasCheckBox.setChecked(False)
		self.ui.otherLineEdit.clear()

		if not self.ui.referenceUnknownCheckBox.isChecked():
			self.ui.referenceUnknownCheckBox.toggle()
		if not self.ui.temperatureUnknownCheckBox.isChecked():
			self.ui.temperatureUnknownCheckBox.toggle()
		if not self.ui.pressureUnknownCheckBox.isChecked():
			self.ui.pressureUnknownCheckBox.toggle()
		if not self.ui.timeUnknownCheckBox.isChecked():
			self.ui.timeUnknownCheckBox.toggle()
		if not self.ui.pHUnknownCheckBox.isChecked():
			self.ui.pHUnknownCheckBox.toggle()
		if not self.ui.solventUnknownCheckBox.isChecked():
			self.ui.solventUnknownCheckBox.toggle()
		if not self.ui.otherUnknownCheckBox.isChecked():
			self.ui.otherUnknownCheckBox.toggle()

		self.ui.updatePushButton.hide()

	def insertReaction(self):

		mess = ''
		if not len(self.reactants):
			mess += "No reactants provided\n"
		if not len(self.products):
			mess += "No products provided\n"
		if not len(str(self.ui.referenceLineEdit.text())) or self.ui.referenceUnknownCheckBox.isChecked():
			mess += "No reference provided\n"

		if mess:
			self.msgDialog = QtGui.QMessageBox(QtGui.QMessageBox.Warning, "Format Error", mess, QtGui.QMessageBox.Ignore)
			self.msgDialog.addButton(QtGui.QMessageBox.Cancel)
			ret = self.msgDialog.exec_()
			if ret == 4194304: #seems to be a magic number... bug in pyqt?
				return

		self.reaction = self.reactionClass()
		self.reaction.insert()

		self.updateReaction()
		
		if not self.readonly:
			self.ui.updatePushButton.show()

		self.msgDialog = QtGui.QMessageBox(QtGui.QMessageBox.Information, "Reaction added", "Reaction added successfully", QtGui.QMessageBox.Ok)
		self.msgDialog.show()
		
	def updateReactionAction(self):
		self.msgDialog = QtGui.QMessageBox(QtGui.QMessageBox.Warning, "Warning", "Reaction updating not supported", QtGui.QMessageBox.Ok)
		self.msgDialog.exec_()
		return
		
		self.updateReaction()

	def updateReaction(self):

		if not self.ui.temperatureUnknownCheckBox.isChecked():
			self.reaction.temperature = self.ui.temperatureSpinBox.value()
		if not self.ui.pressureUnknownCheckBox.isChecked():
			self.reaction.pressure = self.ui.pressureSpinBox.value() / pressureConversions[self.units]
		if not self.ui.timeUnknownCheckBox.isChecked():
			self.reaction.time = self.ui.timeSpinBox.value()
		if not self.ui.pHUnknownCheckBox.isChecked():
			self.reaction.pH = self.ui.pHSpinBox.value()
		if not self.ui.solventUnknownCheckBox.isChecked():
			self.reaction.solvent = self.ui.solventLineEdit.text()
		if self.ui.solidCheckBox.isChecked():
			self.reaction.solid = True
		if self.ui.liquidCheckBox.isChecked():
			self.reaction.liquid = True
		if self.ui.gasCheckBox.isChecked():
			self.reaction.gas = True
		if not self.ui.otherUnknownCheckBox.isChecked():
			self.reaction.other = self.ui.otherLineEdit.text()

		for i in range(len(self.products)):
			for j in range(len(self.reactants)):
				#TODO check for ones that should be deleted
				relation = self.relationClass()
				relation.reactant = self.reactants[i]
				relation.product = self.products[j]
				relation.productYield = self.productPercents[i]
				relation.reactantPercent = self.reactantPercents[j]
				relation.reaction = self.reaction
				relation.insert()

		for catalyst in self.catalysts:
			catalyst.reaction = self.reaction

		if not self.ui.referenceUnknownCheckBox.isChecked():
			if self.reference:
				self.reaction.reference = self.reference

	def search(self):
		self.reaction = self.reactionClass()
		self.reaction.reactants = self.reactants

	def setCurrentReaction(self, reaction):
		self.reaction = reaction
		self.reactants = reaction.reactants
		self.products = reaction.products
		self.catalysts = reaction.catalysts

		self.updateFields()
		
		if not self.readonly:
			self.ui.updatePushButton.show()


	def updateFields(self):
		self.ui.reactantList.addItems([i.name for i in self.reactants])
		self.ui.productList.addItems([i.name for i in self.products])
		self.ui.catalystList.addItems([i.name for i in self.catalysts])

		if self.reaction.reference.id:
			self.ui.referenceLineEdit.setText(self.reaction.reference.title)
			self.ui.referenceUnknownCheckBox.setChecked(False)
		if self.reaction.temperature:
			self.ui.temperatureSpinBox.setValue(self.reaction.temperature)
			self.ui.temperatureUnknownCheckBox.setChecked(False)
		else:
			self.ui.temperatureUnknownCheckBox.setChecked(True)
		if self.reaction.pressure:
			self.ui.pressureSpinBox.setValue(self.reaction.pressure)
			self.ui.pressureUnitsComboBox.setCurrentIndex(0)
			self.ui.pressureUnknownCheckBox.setChecked(False)
		else:
			self.ui.pressureUnknownCheckBox.setChecked(True)
		if self.reaction.time:
			self.ui.timeSpinBox.setValue(self.reaction.time)
			self.ui.timeUnknownCheckBox.setChecked(False)
		else:
			self.ui.timeUnknownCheckBox.setChecked(True)
		if self.reaction.pH:
			self.ui.pHSpinBox.setValue(self.reaction.pH)
			self.ui.pHUnknownCheckBox.setChecked(False)
		else:
			self.ui.pHUnknownCheckBox.setChecked(True)
		if self.reaction.solvent:
			self.ui.solventLineEdit.setText(self.solvent)
			self.ui.solventUnknownCheckBox.setChecked(False)
		else:
			self.ui.solventUnknownCheckBox.setChecked(True)
		if self.reaction.solid:
			self.ui.solidCheckBox.setChecked(True)
		if self.reaction.liquid:
			self.ui.liquidCheckBox.setChecked(True)
		if self.reaction.gas:
			self.ui.gasCheckBox.setChecked(True)
		if self.reaction.other:
			self.ui.otherLineEdit.setText(self.reaction.other)
			self.ui.otherUnknownCheckBox.setChecked(False)
		else:
			self.ui.otherUnknownCheckBox.setChecked(True)

	def setReadonly(self, readonly):
		self.readonly = readonly

		self.ui.temperatureSpinBox.setReadOnly(readonly)
		self.ui.pressureSpinBox.setReadOnly(readonly)
		self.ui.timeSpinBox.setReadOnly(readonly)
		self.ui.pHSpinBox.setReadOnly(readonly)
		self.ui.solventLineEdit.setReadOnly(readonly)
		self.ui.solidCheckBox.setEnabled(not readonly)
		self.ui.liquidCheckBox.setEnabled(not readonly)
		self.ui.gasCheckBox.setEnabled(not readonly)
		self.ui.otherLineEdit.setReadOnly(readonly)

		self.ui.temperatureUnknownCheckBox.setEnabled(not readonly)
		self.ui.pressureUnknownCheckBox.setEnabled(not readonly)
		self.ui.timeUnknownCheckBox.setEnabled(not readonly)
		self.ui.pHUnknownCheckBox.setEnabled(not readonly)
		self.ui.solventUnknownCheckBox.setEnabled(not readonly)
		self.ui.otherUnknownCheckBox.setEnabled(not readonly)

		self.ui.searchCatalystPushButton.setEnabled(not readonly)
		self.ui.searchProductPushButton.setEnabled(not readonly)
		self.ui.searchReactantPushButton.setEnabled(not readonly)
		self.ui.removeCatalystPushButton.setEnabled(not readonly)
		self.ui.removeProductPushButton.setEnabled(not readonly)
		self.ui.removeReactantPushButton.setEnabled(not readonly)
		self.ui.searchReferencePushButton.setEnabled(not readonly)
		self.ui.newCatalystPushButton.setEnabled(not readonly)
		self.ui.newReferencePushButton.setEnabled(not readonly)

		self.ui.addPushButton.setEnabled(not readonly)
		
		if readonly:
			self.ui.updatePushButton.hide()
		else:
			self.ui.updatePushButton.show()

	def setMode(self, mode):
		self.mode = mode
		if self.mode == "New":
			self.ui.addPushButton.show()
			self.ui.updatePushButton.hide()
		elif self.mode == "Update":
			self.ui.addPushButton.hide()
			if self.readonly:
				self.ui.updatePushButton.hide()
			else:
				self.ui.updatePushButton.show()

	def searchCatalyst(self):
		self.controller.catalystDialog("Search")

	def newCatalyst(self):
		self.controller.catalystDialog("New")

	def showAllCatalysts(self):
		catalyst = self.catalystClass()
		catalyst.name = "%"
		self.controller.searchCatalyst(catalyst)

	def searchProduct(self):
		self.searchCompound("Product")

	def searchReactant(self):
		self.searchCompound("Reactant")

	def searchCompound(self, mode):
		self.controller.searchCompoundDialog(mode)

	def addToList(self, obj, where):
		if where == "Reactant":
			if obj in self.reactants:
				msgDialog = QtGui.QMessageBox(QtGui.QMessageBox.Critical, "Adding Error", "Compound already in list", QtGui.QMessageBox.Ok)
				msgDialog.exec_()
				return
			self.reactants.append(obj)
			self.reactantPercents.append(0)
			self.ui.reactantList.addItem(obj.name + "\t0% by mol")
		elif where == "Product":
			if obj in self.products:
				msgDialog = QtGui.QMessageBox(QtGui.QMessageBox.Critical, "Adding Error", "Compound already in list", QtGui.QMessageBox.Ok)
				msgDialog.exec_()
				return
			self.products.append(obj)
			self.productPercents.append(0)
			self.ui.productList.addItem(obj.name + "\t0% yield")
		elif where == "Catalyst":
			if obj in self.catalysts:
				msgDialog = QtGui.QMessageBox(QtGui.QMessageBox.Critical, "Adding Error", "Catalyst already in list", QtGui.QMessageBox.Ok)
				msgDialog.exec_()
				return
			self.catalysts.append(obj)
			self.ui.catalystList.addItem(obj.name)
		elif where == "Reference":
			self.reference = obj
			self.ui.referenceLineEdit.setText(obj.title)

	def searchReference(self):
		self.controller.referenceDialog("Search")

	def newReference(self):
		self.controller.referenceDialog("New")

	def removeReactant(self):
		row = self.ui.reactantList.currentRow()
		del self.reactants[row]
		self.ui.reactantList.takeItem(row)

	def removeProduct(self):
		row = self.ui.productList.currentRow()
		del self.products[row]
		self.ui.productList.takeItem(row)

	def removeCatalyst(self):
		row = self.ui.catalystList.currentRow()
		del self.catalysts[row]
		self.ui.catalystList.takeItem(row)

	def pressureUnitChanged(self, units):
		units = str(units)
		pressure = float(self.ui.pressureSpinBox.value()) / pressureConversions[self.units]
		pressure *= pressureConversions[units]
		self.units = units
		self.ui.pressureSpinBox.setValue(pressure)

	def reactantDoubleClick(self, listItem):
		row = self.ui.reactantList.row(listItem)
		reactant = self.reactants[row]
		name = reactant.name
		oldPercent = self.reactantPercents[row]
		percent,ok = QtGui.QInputDialog.getInteger(self, "Percent Composition", "Please input the mol % of " + name, oldPercent, 0, 100, 1)
		if not ok:
			return
		print percent, sum(self.reactantPercents, percent-oldPercent)

		if sum(self.reactantPercents, percent-oldPercent) > 100:
			msgDialog = QtGui.QMessageBox(QtGui.QMessageBox.Critical, "Format Error", "Percents add to more than 100", QtGui.QMessageBox.Ok)
			msgDialog.exec_()
			return

		self.reactantPercents[row] = percent
		listItem.setText(name + "\t%i%% by mol" % (percent))

	def productDoubleClick(self, listItem):
		row = self.ui.productList.row(listItem)
		product = self.products[row]
		name = product.name
		oldPercent = self.productPercents[row]
		percent,ok = QtGui.QInputDialog.getInteger(self, "Percent Composition", "Please input the % yield of " + name, oldPercent, 0, 100, 1)
		if not ok:
			return
		print percent, sum(self.productPercents, percent-oldPercent)

		if sum(self.productPercents, percent-oldPercent) > 100:
			msgDialog = QtGui.QMessageBox(QtGui.QMessageBox.Critical, "Format Error", "Percents add to more than 100", QtGui.QMessageBox.Ok)
			msgDialog.exec_()
			return

		self.productPercents[row] = percent
		listItem.setText(name + "\t%i%% yield" % (percent))

	# These are hacks so I don't have to write new classes
	def customReactantDropEvent(self, event):
		searchVeiw = event.source().parent()
		if searchVeiw.mode == "Adding Reactant" or searchVeiw.mode == "Adding Product":
			compound = searchVeiw.getSelected()
			self.addToList(compound, "Reactant")

	def customProductDropEvent(self, event):
		searchVeiw = event.source().parent()
		if searchVeiw.mode == "Adding Reactant" or searchVeiw.mode == "Adding Product":
			compound = searchVeiw.getSelected()
			self.addToList(compound, "Product")

	def customCatalystDropEvent(self, event):
		searchVeiw = event.source().parent()
		if searchVeiw.mode == "Adding Catalyst":
			compound = searchVeiw.getSelected()
			self.addToList(compound, "Catalyst")